/*
 *  Copyright (C) 2004 Cidero, Inc.
 *
 *  Permission is hereby granted to any person obtaining a copy of 
 *  this software to use, copy, modify, merge, publish, and distribute
 *  the software for any non-commercial purpose, subject to the
 *  following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY IN CONNECTION WITH THE SOFTWARE.
 * 
 *  File: $RCSfile: SlimStateModel.java,v $
 *
 */

package com.cidero.bridge.slim;

import java.util.logging.Logger;

import com.cidero.upnp.AVTransport;
import com.cidero.upnp.RendererStateModel;

/**
 *  slim renderer state class.  The Slim supports a bunch of 
 *  GET commands to retrieve status from the device. The only one
 *  supported so far here is the GET AUDIO_VOLUME request.
 *
 *  There is no separate support for mute (GET AUDIO_MUTE) - when the
 *  Slim is muted via the remote, a GET AUDIO_VOLUME returns a 0.
 *  This class emulates a Mute feature by making the assumption that
 *  a returned volume of 0 that was not caused by a UPnP command wa
 *  the result of a 'Mute' remote button press.  Cheesy, but it mostly
 *  works as expected.
 * 
 *  Response to volume request 'GET AUDIO_VOLUME'
 *
 *    261 [seqNum] OK value= "XX"
 *
 *
 */
public class SlimStateModel extends RendererStateModel
{
  private static Logger logger = 
    Logger.getLogger("com.cidero.bridge.slim");

  public SlimStateModel()
  {
  }
  
  public void setChanged()
  {
    super.setChanged();
  }
  
  /**
   *  Parse a single-line response to a GET_XXX request
   */
  public boolean parseResponse( String response )
  {
    if( response == null )
    {
      logger.warning("Status response syntax error ");
      return false;
    }
    
    if( response.startsWith("261") )  // Response to GET AUDIO_VOLUME
    {
      String[] tmp = response.split("=");
      // strip quotes off volume value
      int firstQuoteIndex = tmp[1].indexOf("\"");
      int lastQuoteIndex = tmp[1].lastIndexOf("\"");
      if( (firstQuoteIndex >= 0) &&  (lastQuoteIndex >= 0) && 
          (firstQuoteIndex != lastQuoteIndex) )
      {
        String value = tmp[1].substring( firstQuoteIndex+1, lastQuoteIndex );

        // If volume changed to 0 due to Slim remote, it's most likely
        // a mute button press. Leave the volume alone and set the mute 
        // state to TRUE
        if( value.equals("0") && (getVolume() != 0) )
        {
          setMute("1");
        }
        else
        {
          // non-zero volume - clear Mute condition 
          setMute("0");
          setVolume( value );
        }
      }
    }
    else
    {
      logger.fine("Status response '" + response + "' discarded" );
    }
    
    return true;
  }

  /**
   *  The Slim outputs a set of asynchronous messages on the media agent
   *  TCP connection. This listener interface allows them to be acted upon.
   *
   *  The message codes are:
   *
   *   905       Automatic timestamp/PTS messages
   *   907/908   Notify on new Shoutcast song/title
   *   909       Notify when media length known
   *   910/911   Notify on audio/video underflow
   *   912       Notify on invalid audio data
   *    
   *   Note that additional text follows the message code. See Slim
   *   media agent doc for more details
   *
   *   Sample msg:   "910 Audio underflow"
   */
  public boolean parseNotifyMsg( String msg )
  {
    logger.fine("parsing notify " + msg );

    if( msg.startsWith("905") )  
    {
      //
      // Timestamp message - use this to trigger event, so the 
      // renderer GUI (control point) will request position info
      // Only look at every other one since the slim outputs
      // them at ~2Hz, and the GUI only needs a 1 Hz refresh
      //

      //System.out.println("Got TIMESTAMP notification");
      
      //      timeStampCount++;
      //      if( (timeStampCount % 2) == 0 )
      //      {
      //        avTransport.updateStateVariable( "RelativeTimePosition",
      //                                         "00:00:01" );
      //      }
    }
    else if( msg.startsWith("910") || msg.startsWith("911") )
    {
      // Underflow

      // If multiple underflows occur in a short space of time, declare
      // end of media - switch to stopped state
      //avTransport.processUnderflow( msg );
    }
    else if( msg.startsWith("902") )
    {
      // End of stream (TCP/IP connection broken, normally by server side

      setTransportState( AVTransport.STATE_STOPPED );
      
      //
      // Need to send a STOP command to slim to prevent endless underflow 
      // messages (why doesn't PRISMIQ do this?)
      //
      //mediaRenderer.stop();

    }
    else if( msg.startsWith("907") || msg.startsWith("908") )
    {
      // New shoutcast song title
    }
          
    // Other 9XX responses are discarded

    return true;
  }

  /**
   * Simple test code
   */
  public static void main( String args[] )
  {
    try 
    {
      SlimStateModel status = new SlimStateModel();
      status.parseResponse( "261 [1] OK value= \"95\"" );
      System.out.println("Volume = " + status.getVolume() );
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
    
  }

}


